/**
 * 方法符号引用
 * User: zhikang.peng
 * Date: 2018/04/27 14:53
 */
package heap

import "gitee.com/thghh/jvmgo/classfile"

type MethodRef struct {
	MemberRef
	method *Method
}

func newMethodRef(cp *ConstantPool, refInfo *classfile.ConstantMethodrefInfo) *MethodRef {
	ref := &MethodRef{}
	ref.cp = cp
	ref.copyMemberRefInfo(&refInfo.ConstantMemberrefInfo)
	return ref
}

func (self *MethodRef) ResolvedMethod() *Method {
	if self.method == nil {
		self.resolveMethodRef()
	}
	return self.method
}

/**
D想通过方法符号引用访问类C的某个方法，先要解析符号引用得到类C。
如果C是接口，则抛出IncompatibleClassChangeError异常，
否则根据方法名和描述符查找方法。如果找不到对应的方法，
则抛出NoSuchMethodError异常，否则检查类D是否有权限访问该方法。
如果没有，则抛出IllegalAccessError异常
*/
func (self *MethodRef) resolveMethodRef() {
	d := self.cp.class
	c := self.ResolvedClass()
	if c.IsInterface() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	method := lookupMethod(c, self.name, self.descriptor)
	if method == nil {
		panic("java.lang.NoSuchMethodError")
	}
	if !method.isAccessibleTo(d) {
		panic("java.lang.IllegalAccessError")
	}
	self.method = method
}

// 从类中寻找指定的方法
func lookupMethod(class *Class, name string, descriptor string) *Method {
	method := LookupMethodInClass(class, name, descriptor)
	if method == nil {
		method = LookupMethodInInterfaces(class.interfaces, name, descriptor)
	}
	return method
}

func LookupMethodInClass(class *Class, name string, descriptor string) *Method {
	for c := class; c != nil; c = c.superClass {
		for _, method := range c.methods {
			if method.name == name && method.descriptor == descriptor {
				return method
			}
		}
	}
	return nil
}

// 从接口class或父接口中寻找符合的Method
func LookupMethodInInterface(iface *Class, name string, descriptor string) *Method {
	for _, method := range iface.methods {
		if method.name == name && method.descriptor == descriptor {
			return method
		}
	}
	return LookupMethodInInterfaces(iface.interfaces, name, descriptor)
}

// 从接口数组class或父接口中寻找符合的Method
func LookupMethodInInterfaces(ifaces []*Class, name string, descriptor string) *Method {
	for _, iface := range ifaces {
		for _, method := range iface.methods {
			if method.name == name && method.descriptor == descriptor {
				return method
			}
		}
		method := LookupMethodInInterfaces(iface.interfaces, name, descriptor)
		if method != nil {
			return method
		}
	}
	return nil
}
